package com.cdg.runyunyijia;

import static org.eclipse.paho.client.mqttv3.MqttException.REASON_CODE_CLIENT_DISCONNECTING;
import static org.eclipse.paho.client.mqttv3.MqttException.REASON_CODE_CONNECT_IN_PROGRESS;

import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.Color;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;

import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.appcompat.app.AppCompatActivity;
import androidx.preference.PreferenceManager;

import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
import org.eclipse.paho.client.mqttv3.MqttCallback;
import org.eclipse.paho.client.mqttv3.MqttClient;
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.UnsupportedEncodingException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

class ItemInfo {
    public int id;
    public String name;
    public long last_time;
}

class CBafayunSetting {
    public String url;
    public String username;
    public Map<String, ItemInfo> list_Topic = new ConcurrentHashMap<>();
    public Map<Integer, String> list_topicId = new ConcurrentHashMap<>();
}

public class MainActivity extends AppCompatActivity {

    private final CBafayunSetting BafayunSetting = new CBafayunSetting();
    private ScheduledExecutorService scheduler, scheduler2/**/;

    private MqttClient client;
    private MqttConnectOptions options;
    //private TextView text_powerState, text_time, text_name, text_is_online;
    private final String Log_Tag = "MQTT MSG";
    private final Handler handler = new Handler(Looper.getMainLooper(), new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            switch (MSG_ID.values()[msg.what]) {
                case MSG_MSGCALLBACK:
                    String message = msg.getData().getString("message");
                    Log.d(Log_Tag, msg.toString());
                    Updata(msg.obj.toString(), message);
                    break;
                case MSG_CONNECTERROR:  //连接失败
                    Toast.makeText(MainActivity.this, "连接失败:" + msg.obj.toString(), Toast.LENGTH_SHORT).show();
                    Log.d(Log_Tag, "连接失败");
                    ((TextView) findViewById(R.id.bafa_state)).setText("巴法云连接失败,正在自动重连...");
                    ((TextView) findViewById(R.id.bafa_state)).setTextColor(Color.rgb(255, 0, 0));
                    break;
                case MSG_CONNECTSUCESSED: //连接成功
                    Toast.makeText(MainActivity.this, "连接成功", Toast.LENGTH_SHORT).show();
                    Log.d(Log_Tag, "连接成功");
                    ((TextView) findViewById(R.id.bafa_state)).setText("巴法云连接成功");
                    ((TextView) findViewById(R.id.bafa_state)).setTextColor(Color.rgb(0, 255, 0));
                    //订阅主题
                    try {
                        for (String ite : BafayunSetting.list_Topic.keySet()) {
                            client.subscribe(ite, 1);
                            publishmessage(ite, "isonline");
                        }
                        Log.d(Log_Tag, "订阅成功");
                    } catch (MqttException e) {
                        e.printStackTrace();
                        Log.d(Log_Tag, "订阅失败");
                    }
                    break;
                case MSG_CHECKDEVONLINE:
                    //Updata(msg.obj.toString(), message);
                    UpdateOnline((int)msg.obj, msg.arg1 == 0 ? (msg.arg2 == 1) : false);
                    break;
                default:
                    break;
            }
            return true;
        }
    });

    private void InitData() {
        SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
        BafayunSetting.url = sharedPreferences.getString("bafaurl", "tcp://bemfa.com:9501");
        BafayunSetting.username = sharedPreferences.getString("bafakey", "");
        BafayunSetting.list_Topic.clear();
        BafayunSetting.list_topicId.clear();
        String topics = sharedPreferences.getString("bafa_topic", "");
        if (topics.isEmpty())
            return;
        int idx = 1000;
        for (String ite : topics.split(";")) {
            String[] topic = ite.split("\\|");
            if (topic[0].isEmpty())
                continue;

            ItemInfo itemInfo = new ItemInfo();

            itemInfo.name = (topic.length == 2) ? topic[1] : "未命名";
            itemInfo.id = idx++;

            BafayunSetting.list_Topic.put(topic[0], itemInfo);
            BafayunSetting.list_topicId.put(itemInfo.id, topic[0]);
        }
    }

    private boolean CheckBafaSetting() {
        return (BafayunSetting.url.isEmpty() || BafayunSetting.username.isEmpty() || BafayunSetting.list_Topic.isEmpty()) != true;
    }

    private String getStringByStream(InputStream inputStream) {

        try {
            Reader reader = new InputStreamReader(inputStream, "UTF-8");
            char[] rawBuffer = new char[512];
            StringBuffer buffer = new StringBuffer();
            int length = 0;
            while ((length = reader.read(rawBuffer)) != -1) {
                buffer.append(rawBuffer, 0, length);
            }
            return buffer.toString();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    /*private void CheckTopicIsOnline(String uid, String topic, int type) {
        new Thread(
                () -> {
                    try {
                        String strurl = String.format("https://apis.bemfa.com/va/online?uid=%s&topic=%s&type=%d", uid, topic, type);
                        URL url = new URL(strurl);
                        HttpURLConnection connection = (HttpURLConnection) url.openConnection();
                        connection.setConnectTimeout(3000);
                        connection.setReadTimeout(3000);
                        //设置请求方式 GET / POST 一定要大小
                        connection.setRequestMethod("GET");
                        connection.setDoInput(true);
                        connection.setDoOutput(false);
                        connection.connect();
                        int responseCode = connection.getResponseCode();
                        if (responseCode != HttpURLConnection.HTTP_OK) {
                            throw new IOException("HTTP error code" + responseCode);
                        }
                        String result = getStringByStream(connection.getInputStream());
                        if (result == null) {
                            Log.d("Fail", "失败了");
                        } else {
                            /**
                             * {
                             *     "code": 0,
                             *     "message": "OK",
                             *     "data": false
                             * }
                             *code 字段说明：
                             *
                             * 状态码	说明
                             * 0	成功text_is_online
                             * 10002	请求参数有误
                             * 40000	未知错误
                             * 40004	私钥或主题错误
                             * data 字段说明：
                             *
                             * 状态码	说明
                             * true	设备在线
                             * false	设备离线


                            try {
                                JSONObject jsonobject = new JSONObject(result);
                                int code = jsonobject.optInt("code");
                                boolean data = jsonobject.optBoolean("data");
                                Message msg = new Message();
                                msg.what = MSG_ID.MSG_CHECKDEVONLINE.ordinal();
                                msg.obj = topic;
                                msg.arg1 = code;
                                msg.arg2 = data ? 1 : 0;
                                handler.sendMessage(msg);
                            } catch (JSONException e) {
                                e.printStackTrace();
                            }
                            Log.d("succuss", "成功了 ");
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }).start();
    }*/

    enum MSG_ID {
        MSG_MSGCALLBACK, MSG_CONNECTERROR, MSG_CONNECTSUCESSED, MSG_CHECKDEVONLINE,
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.settingbtn, menu);
        return true;
    }

    ActivityResultLauncher<Intent> intentActivityResultLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(),
            result -> {
                //此处是跳转的result回调方法
                //if ( result.getResultCode() == Activity.RESULT_OK) {
                try {
                    onInit();
                } catch (MqttException e) {
                    e.printStackTrace();
                }
                InitContrl();
                //} else {
                //    Toast.makeText(getApplicationContext(), R.string.cacel, Toast.LENGTH_LONG).show();
                //}
            });

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case R.id.action_setting://监听菜单按钮
                Intent intent = new Intent(MainActivity.this, SettingsActivity.class);
                intentActivityResultLauncher.launch(intent);
                break;
            case R.id.all_devota://监听菜单按钮
                PushAllDevMsg("ota");
                break;
            case R.id.all_poweron://监听菜单按钮
                PushAllDevMsg("on");
                break;
            case R.id.all_poweroff://监听菜单按钮
                PushAllDevMsg("off");
                break;
        }
        return super.onOptionsItemSelected(item);
    }

    private void onInit() throws MqttException {
        InitData();
        if (!CheckBafaSetting())
            return;

        Mqtt_init();

        if (client.isConnected())
            client.disconnect();
        Mqtt_connect();
        startCheckDevOnline();
    }

    private void PushAllDevMsg(String msg) {
        if (msg.isEmpty())
            return;
        for (String key : BafayunSetting.list_Topic.keySet()) {
            publishmessage(key, msg);
        }
    }

    private void InitContrl() {
        ViewGroup Root = findViewById(R.id.root_linearLayout);
        Root.removeAllViews();

        for (Map.Entry<Integer, String> entry : BafayunSetting.list_topicId.entrySet()) {

            View v1 = LayoutInflater.from(this).inflate(R.layout.layout_pcctrl, Root, false);
            v1.setId(entry.getKey());

            TextView text_name = v1.findViewById(R.id.textName);
            String name = entry.getValue();
            text_name.setText(BafayunSetting.list_Topic.get(entry.getValue()).name);

            Root.addView(v1, -1);
            Button btn_power = v1.findViewById(R.id.btn_on);
            btn_power.setTag(entry.getKey());
            btn_power.setOnClickListener(v -> {
                int parentId = (int) v.getTag();
                if (BafayunSetting.list_topicId.containsKey(parentId)) {
                    Log.d("btn listener:", "btn is clicked!");
                    publishmessage(BafayunSetting.list_topicId.get(parentId), "on");
                }
            });
            /*btn_power.setOnLongClickListener(v -> {
                int parentId = (int) v.getTag();
                if (BafayunSetting.list_topicId.containsKey(parentId)) {
                    Log.d("btn listener:", "btn is longClicked!");
                    publishmessage(BafayunSetting.list_topicId.get(parentId), "longpresspower");
                }
                return true;
            });*/
            Button btn_reboot = v1.findViewById(R.id.btn_off);
            btn_reboot.setTag(entry.getKey());
            btn_reboot.setOnClickListener(v -> {
                int parentId = (int) v.getTag();
                if (BafayunSetting.list_topicId.containsKey(parentId)) {
                    Log.d("btn listener:", "btn is clicked!");
                    publishmessage(BafayunSetting.list_topicId.get(parentId), "off");
                }
            });


            Button btn_getconfig = v1.findViewById(R.id.btn_getconfig);
            btn_getconfig.setTag(entry.getKey());
            btn_getconfig.setOnClickListener(v -> {
                int parentId = (int) v.getTag();
                if (BafayunSetting.list_topicId.containsKey(parentId)) {
                    Log.d("btn listener:", "btn is clicked!");
                    publishmessage(BafayunSetting.list_topicId.get(parentId), "getconfig");
                }
            });

            Button btn_setconfig = v1.findViewById(R.id.btn_setconfig);
            btn_setconfig.setTag(entry.getKey());
            btn_setconfig.setOnClickListener(v -> {
                int parentId = (int) v.getTag();
                if (BafayunSetting.list_topicId.containsKey(parentId)) {
                    Log.d("btn listener:", "btn is clicked!");

                    EditText et_delay = ((LinearLayout) v.getParent()).findViewById(R.id.et_delay);
                    if (et_delay != null) {
                        publishmessage(BafayunSetting.list_topicId.get(parentId), "delay:" + et_delay.getText().toString());
                    }
                }
            });
        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        /*text_name = findViewById(R.id.name);
        text_powerState = findViewById(R.id.powerState);
        text_time = findViewById(R.id.time);
        text_is_online = findViewById(R.id.is_online);*/
        try {
            onInit();
        } catch (MqttException e) {
            e.printStackTrace();
        }
        InitContrl();
    }

    private void Updata(String topic, String msg) {
        if (BafayunSetting.list_Topic.containsKey(topic)) {

            ItemInfo topic_ref = BafayunSetting.list_Topic.get(topic);
            assert topic_ref != null;
            int id = topic_ref.id;
            topic_ref.last_time = System.currentTimeMillis();
            View pcView = findViewById(id);
            if (pcView != null) {
                DateFormat df = new SimpleDateFormat("HH:mm:ss");
                TextView text_powerState = pcView.findViewById(R.id.powerState);
                ((TextView) pcView.findViewById(R.id.is_online)).setText("设备在线");
                TextView text_time = pcView.findViewById(R.id.textTime);
                text_time.setText(df.format(new Date()));
                int parseIndex = msg.indexOf(":");
                if (parseIndex != -1) {
                    String cmd = msg.substring(0, parseIndex);
                    String arg = msg.substring(parseIndex + 1);
                    switch (cmd) {
                        case "delay": {
                            if (!arg.isEmpty()) {
                                ((EditText) pcView.findViewById(R.id.et_delay)).setText(arg);
                            }
                        }
                        break;
                    }
                } else
                    text_powerState.setText(msg.equals("true") ? "设备运行" : "设备停止");
            }
        }
    }

    private void UpdateOnline(String topic, boolean isOnline) {

        if (BafayunSetting.list_Topic.containsKey(topic)) {
            int id = BafayunSetting.list_Topic.get(topic).id;
            View pcView = findViewById(id);
            if (pcView != null) {
                TextView text_is_online = pcView.findViewById(R.id.is_online);
                text_is_online.setText(isOnline ? "设备在线" : "设备离线");
            }
        }
    }

    private void UpdateOnline(int topic, boolean isOnline) {
        View pcView = findViewById(topic);
        if (pcView != null) {
            TextView text_is_online = pcView.findViewById(R.id.is_online);
            text_is_online.setText(isOnline ? "设备在线" : "设备离线");
        }
    }

    private void Mqtt_init() {
        try {
            //host为主机名，test为clientid即连接MQTT的客户端ID，一般以客户端唯一标识符表示，MemoryPersistence设置clientid的保存形式，默认为以内存保存
            client = new MqttClient(BafayunSetting.url, BafayunSetting.username, new MemoryPersistence());
            //MQTT的连接设置
            options = new MqttConnectOptions();
            //设置是否清空session,这里如果设置为false表示服务器会保留客户端的连接记录，这里设置为true表示每次连接到服务器都以新的身份连接
            options.setCleanSession(false);
            options.setUserName(BafayunSetting.username);
            //options.setPassword(BafayunSetting.password.toCharArray());
            // 设置超时时间 单位为秒
            options.setConnectionTimeout(10);
            // 设置会话心跳时间 单位为秒 服务器会每隔1.5*20秒的时间向客户端发送个消息判断客户端是否在线，但这个方法并没有重连的机制
            options.setKeepAliveInterval(20);
            //重新连接只是重新连接，并不会自动订阅
            //options.setAutomaticReconnect(true);
            //设置回调
            client.setCallback(new MqttCallback() {
                @Override
                public void connectionLost(Throwable cause) {
                    //连接丢失后，一般在这里面进行重连
                    Log.d(Log_Tag, "连接丢失");
                    ((TextView) findViewById(R.id.bafa_state)).setText("巴法云连接丢失，自动重连中...");
                    ((TextView) findViewById(R.id.bafa_state)).setTextColor(Color.rgb(255, 0, 0));
                    startReconnect();
                }

                @Override
                public void messageArrived(String topicName, MqttMessage message) throws Exception {
                    //subscribe后得到的消息会执行到这里面
                    Log.d(Log_Tag, "消息到达");
                    String strMsg = message.toString();
                    //只处理以S:开头的消息表明是从设备发送的消息
                    if (strMsg.startsWith("S:") && strMsg.length() > 2) {
                        Message msg = new Message();
                        //封装message包
                        msg.what = MSG_ID.MSG_MSGCALLBACK.ordinal();   //收到消息标志位
                        msg.obj = topicName;
                        Bundle bundle = new Bundle();
                        bundle.putString("message", strMsg.substring(2));
                        msg.setData(bundle);
                        //发送messge到handler
                        handler.sendMessage(msg);
                    }
                }

                @Override
                public void deliveryComplete(IMqttDeliveryToken token) {
                    //publish后会执行到这里
                    Log.d(Log_Tag, "推送完成:" + token.isComplete());
                }
            });
        } catch (Exception e) {
            e.printStackTrace();
            Log.e(Log_Tag, e.getMessage());
        }
    }

    /***
     * MQTT 连接
     */
    private void Mqtt_connect() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    if (!(client.isConnected()))  //如果还未连接
                    {
                        client.connect(options);
                        Message msg = new Message();
                        msg.what = MSG_ID.MSG_CONNECTSUCESSED.ordinal();
                        handler.sendMessage(msg);
                    }
                } catch (MqttException e) {
                    switch (e.getReasonCode()) {
                        case REASON_CODE_CONNECT_IN_PROGRESS:
                            break;
                        case REASON_CODE_CLIENT_DISCONNECTING:
                            break;
                        default: {
                            e.printStackTrace();
                            Message msg = new Message();
                            msg.what = MSG_ID.MSG_CONNECTERROR.ordinal();
                            msg.obj = e.getMessage();
                            handler.sendMessage(msg);
                        }
                    }
                }
            }
        }).start();
    }

    /***
     * 开始重新连接
     */
    private void startReconnect() {
        scheduler = Executors.newSingleThreadScheduledExecutor();
        scheduler.scheduleWithFixedDelay(new Runnable() {
            @Override
            public void run() {
                if (!client.isConnected()) {
                    Mqtt_connect();
                }
            }
        }, 0, 10 * 1000, TimeUnit.MILLISECONDS);
    }

    private void startCheckDevOnline() {
        scheduler2 = Executors.newSingleThreadScheduledExecutor();
        scheduler2.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                if (CheckBafaSetting()) {
                    for (ItemInfo ite : BafayunSetting.list_Topic.values()) {
                        if (System.currentTimeMillis() - ite.last_time > 30 * 1000) {
                            Message msg = new Message();
                            //封装message包
                            msg.what = MSG_ID.MSG_CHECKDEVONLINE.ordinal();   //收到消息标志位
                            msg.obj = ite.id;
                            msg.arg1 = 0;
                            msg.arg2 = 0;
                            //发送messge到handler
                            handler.sendMessage(msg);
                        }
                    }
                }
            }
        }, 0, 10, TimeUnit.SECONDS);
    }

    /***
     * 发布消息加
     * @param topic
     * @param message2
     */
    private void publishmessage(String topic, String message2) {
        if (client == null || !client.isConnected()) {
            return;
        }
        MqttMessage message = new MqttMessage();

        message2 = "C:" + message2;
        message.setPayload(message2.getBytes());
        try {
            client.publish(topic + "/set", message);
        } catch (MqttException e) {
            e.printStackTrace();
        }
    }
}